#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据库操作模拟模块

提供模拟的数据库操作函数，用于在无真实数据库环境下测试多线程同步框架
"""

import random
import time
from typing import List, Dict, Any, Optional


# 模拟数据库表结构配置
# 用于模拟不同表的数据结构和数据量
TABLE_CONFIGS = {
    # 小型表 - 数据量小，操作快
    'small': {
        'min_rows': 100,
        'max_rows': 500,
        'min_columns': 5,
        'max_columns': 10,
        'min_process_time': 0.1,
        'max_process_time': 0.3,
        'error_rate': 0.05  # 5%的错误率
    },
    # 中型表 - 数据量中等，操作时间中等
    'medium': {
        'min_rows': 1000,
        'max_rows': 5000,
        'min_columns': 10,
        'max_columns': 20,
        'min_process_time': 0.3,
        'max_process_time': 1.0,
        'error_rate': 0.1   # 10%的错误率
    },
    # 大型表 - 数据量大，操作时间长
    'large': {
        'min_rows': 10000,
        'max_rows': 50000,
        'min_columns': 20,
        'max_columns': 50,
        'min_process_time': 1.0,
        'max_process_time': 3.0,
        'error_rate': 0.15  # 15%的错误率
    }
}


class DatabaseError(Exception):
    """
    模拟数据库异常类
    """
    pass


class ConnectionError(DatabaseError):
    """
    模拟数据库连接异常
    """
    pass


class TimeoutError(DatabaseError):
    """
    模拟数据库超时异常
    """
    pass


class DataIntegrityError(DatabaseError):
    """
    模拟数据完整性异常
    """
    pass


def _get_table_type(table_name: str) -> str:
    """
    根据表名确定表的类型
    
    为了模拟不同大小的表，我们根据表名的数字部分来确定表的类型
    
    Args:
        table_name: 表名
        
    Returns:
        str: 表类型（small, medium, large）
    """
    # 尝试从表名中提取数字
    try:
        # 假设表名格式为 table_0001, table_0002 等
        table_num = int(table_name.split('_')[-1])
    except (ValueError, IndexError):
        # 如果无法提取数字，默认为中型表
        return 'medium'
    
    # 根据表号分配类型
    # 前100个表为小型表，中间300个为中型表，最后100个为大型表
    if table_num <= 100:
        return 'small'
    elif table_num <= 400:
        return 'medium'
    else:
        return 'large'


def _generate_random_data(num_rows: int, num_columns: int) -> List[Dict[str, Any]]:
    """
    生成随机数据
    
    Args:
        num_rows: 行数
        num_columns: 列数
        
    Returns:
        List[Dict[str, Any]]: 模拟的数据行列表
    """
    data = []
    
    # 生成列名列表
    columns = [f'column_{i}' for i in range(num_columns)]
    
    for i in range(num_rows):
        row = {}
        for col in columns:
            # 根据列索引生成不同类型的数据，增加数据多样性
            col_idx = int(col.split('_')[-1])
            
            if col_idx % 5 == 0:
                row[col] = i  # 整数ID
            elif col_idx % 5 == 1:
                row[col] = f'value_{i}_{col}'  # 字符串
            elif col_idx % 5 == 2:
                row[col] = round(random.uniform(0, 1000), 2)  # 浮点数
            elif col_idx % 5 == 3:
                row[col] = random.choice([True, False])  # 布尔值
            else:
                # 空值，模拟某些字段可能为空
                row[col] = None if random.random() < 0.1 else f'optional_value_{i}'
        
        data.append(row)
    
    return data


def _simulate_error(config: Dict[str, Any], operation: str) -> None:
    """
    模拟数据库操作可能发生的错误
    
    Args:
        config: 表配置字典
        operation: 当前操作类型（fetch或insert）
        
    Raises:
        DatabaseError: 各种模拟的数据库异常
    """
    if random.random() < config['error_rate']:
        # 随机选择一种错误类型
        error_type = random.choice(['connection', 'timeout', 'integrity', 'unknown'])
        
        if error_type == 'connection':
            raise ConnectionError(f"数据库连接错误: 无法连接到源数据库 (操作: {operation})")
        elif error_type == 'timeout':
            raise TimeoutError(f"操作超时: {operation} 操作超过了最大允许时间")
        elif error_type == 'integrity':
            raise DataIntegrityError(f"数据完整性错误: {operation} 操作违反了数据完整性约束")
        else:
            raise DatabaseError(f"未知数据库错误: {operation} 操作失败")


def mock_fetch_data(table_name: str) -> List[Dict[str, Any]]:
    """
    模拟从源数据库拉取数据
    
    Args:
        table_name: 表名
        
    Returns:
        List[Dict[str, Any]]: 拉取的数据列表
        
    Raises:
        DatabaseError: 模拟的数据库操作异常
    """
    # 获取表类型和配置
    table_type = _get_table_type(table_name)
    config = TABLE_CONFIGS[table_type]
    
    # 模拟操作延迟
    process_time = random.uniform(config['min_process_time'], config['max_process_time'])
    
    try:
        # 模拟网络传输和处理时间
        time.sleep(process_time * 0.7)  # 70%的时间用于获取数据
        
        # 模拟可能的错误
        _simulate_error(config, 'fetch')
        
        # 生成随机数据
        num_rows = random.randint(config['min_rows'], config['max_rows'])
        num_columns = random.randint(config['min_columns'], config['max_columns'])
        data = _generate_random_data(num_rows, num_columns)
        
        # 模拟处理时间
        time.sleep(process_time * 0.3)  # 30%的时间用于处理数据
        
        return data
        
    except DatabaseError:
        # 直接重新抛出数据库错误
        raise
    except Exception as e:
        # 包装其他异常为数据库错误
        raise DatabaseError(f"获取表 {table_name} 数据时发生未知错误: {str(e)}")


def mock_insert_data(table_name: str, data: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    模拟向目标数据库插入数据
    
    Args:
        table_name: 表名
        data: 要插入的数据列表
        
    Returns:
        Dict[str, Any]: 插入操作的结果，包含影响的行数等信息
        
    Raises:
        DatabaseError: 模拟的数据库操作异常
    """
    # 获取表类型和配置
    table_type = _get_table_type(table_name)
    config = TABLE_CONFIGS[table_type]
    
    # 根据数据量和表类型计算处理时间
    data_size_factor = min(len(data) / config['max_rows'], 1.0)
    process_time = random.uniform(
        config['min_process_time'] * (0.5 + data_size_factor * 0.5),
        config['max_process_time'] * (0.5 + data_size_factor * 0.5)
    )
    
    try:
        # 模拟预处理时间
        time.sleep(process_time * 0.2)  # 20%的时间用于预处理
        
        # 模拟可能的错误
        _simulate_error(config, 'insert')
        
        # 模拟批量插入过程，分批处理
        batch_size = random.randint(100, 1000)
        total_batches = (len(data) + batch_size - 1) // batch_size
        
        for batch_idx in range(total_batches):
            # 模拟每批次插入
            start_idx = batch_idx * batch_size
            end_idx = min(start_idx + batch_size, len(data))
            batch_data = data[start_idx:end_idx]
            
            # 模拟每批次插入的时间
            batch_time = process_time * 0.6 / total_batches
            time.sleep(batch_time)
        
        # 模拟后处理时间
        time.sleep(process_time * 0.2)  # 20%的时间用于后处理
        
        # 返回插入结果
        return {
            'table': table_name,
            'inserted_rows': len(data),
            'status': 'success',
            'operation_time': process_time
        }
        
    except DatabaseError:
        # 直接重新抛出数据库错误
        raise
    except Exception as e:
        # 包装其他异常为数据库错误
        raise DatabaseError(f"向表 {table_name} 插入数据时发生未知错误: {str(e)}")


def mock_verify_data(table_name: str, expected_count: int) -> Dict[str, Any]:
    """
    模拟验证目标数据库中的数据
    
    Args:
        table_name: 表名
        expected_count: 期望的数据行数
        
    Returns:
        Dict[str, Any]: 验证结果
    """
    # 模拟验证过程
    time.sleep(random.uniform(0.1, 0.5))
    
    # 模拟可能的数据不一致情况（5%概率）
    if random.random() < 0.05:
        actual_count = expected_count + random.randint(-10, 10)
        is_consistent = False
    else:
        actual_count = expected_count
        is_consistent = True
    
    return {
        'table': table_name,
        'expected_count': expected_count,
        'actual_count': actual_count,
        'is_consistent': is_consistent
    }